Phase1:
Proplem:
We live in a time when video games are extremely popular. The global
video game market continues to grow year-on-year, and the industry is
now valued at over $100 billion worldwide. With technology continuously
pushing the boundaries, video games have only become more popular and
more high-quality. Gameplay mechanics, cutting-edge graphics, and
intricate storylines make today’s games more immersive than ever before.
We chose this dataset to gain insights on the popularity of upcoming
games.
Class label:
Popular’ is our class label, we will use Global_Sales attribute to
predict whether a game will sell 1000000 or more globally.
Data Mining Task:
Our data mining task is to predict the popularity of upcoming games
using regression.
Description of the dataset:
The dataset provided by vgchartz.com supply us with a valuable
resource to explore the platforms and genres of the top 16599 global
video games. Through it, we can analyze the most popular platforms and
genres that are influencing global sales, and detectr how regions’ sales
affect global sales.
Our goal:
Our goal from studying this dataset is to utilize regression
techniques on the input data to make predictions about the popularity of
upcoming games.
Attributes description:
| Rank |
Ranking of the game based on global sales. |
Numeric |
| Name |
Name of the game. |
Nominal |
| Platform |
Platform the game was released on. |
Nominal |
| Year |
Year the game was released. |
Ordinal |
| Genre |
Genre of the game |
Nominal |
| Publisher |
Publisher of the game. |
Nominal |
| NA_Sales |
Sales of the game in North America |
Numeric (ratio-scaled) |
| EU_Sales |
Sales of the game in Europe |
Numeric (ratio-scaled) |
| JP_Sales |
Sales of the game in Japan |
Numeric (ratio-scaled) |
| Other_Sales |
Sales of the game in other regions |
Numeric (ratio-scaled) |
| Global_Sales |
Total sales of the game worldwide |
Numeric (ratio-scaled) |
loading libraries needed for our data mining tasks:
library(outliers)
library(dplyr)
library(Hmisc)
library(ggplot2)
library(mlbench)
library(caret)
options(max.print=9999999)
Importing our dataset:
dataset=read.csv("Dataset/vgsales.csv")
General info about our dataset:
checking number of rows and columns, and cheking dimensionality and
coulumns names:
nrow(dataset)
[1] 16598
ncol(dataset)
[1] 11
dim(dataset)
[1] 16598 11
names(dataset)
[1] "Rank" "Name" "Platform" "Year" "Genre" "Publisher" "NA_Sales" "EU_Sales" "JP_Sales" "Other_Sales" "Global_Sales"
Dataset structure:
str(dataset)
'data.frame': 16598 obs. of 11 variables:
$ Rank : int 1 2 3 4 5 6 7 8 9 10 ...
$ Name : chr "Wii Sports" "Super Mario Bros." "Mario Kart Wii" "Wii Sports Resort" ...
$ Platform : chr "Wii" "NES" "Wii" "Wii" ...
$ Year : chr "2006" "1985" "2008" "2009" ...
$ Genre : chr "Sports" "Platform" "Racing" "Sports" ...
$ Publisher : chr "Nintendo" "Nintendo" "Nintendo" "Nintendo" ...
$ NA_Sales : num 41.5 29.1 15.8 15.8 11.3 ...
$ EU_Sales : num 29.02 3.58 12.88 11.01 8.89 ...
$ JP_Sales : num 3.77 6.81 3.79 3.28 10.22 ...
$ Other_Sales : num 8.46 0.77 3.31 2.96 1 0.58 2.9 2.85 2.26 0.47 ...
$ Global_Sales: num 82.7 40.2 35.8 33 31.4 ...
sample of raw dataset(first 10 rows):
head(dataset, 10)
sample of raw dataset(last 10 rows):
tail(dataset, 10)
Five number summary of each attribute in our dataset:
summary(dataset)
Rank Name Platform Year Genre Publisher NA_Sales EU_Sales JP_Sales Other_Sales
Min. : 1 Length:16598 Length:16598 Length:16598 Length:16598 Length:16598 Min. : 0.0000 Min. : 0.0000 Min. : 0.00000 Min. : 0.00000
1st Qu.: 4151 Class :character Class :character Class :character Class :character Class :character 1st Qu.: 0.0000 1st Qu.: 0.0000 1st Qu.: 0.00000 1st Qu.: 0.00000
Median : 8300 Mode :character Mode :character Mode :character Mode :character Mode :character Median : 0.0800 Median : 0.0200 Median : 0.00000 Median : 0.01000
Mean : 8301 Mean : 0.2647 Mean : 0.1467 Mean : 0.07778 Mean : 0.04806
3rd Qu.:12450 3rd Qu.: 0.2400 3rd Qu.: 0.1100 3rd Qu.: 0.04000 3rd Qu.: 0.04000
Max. :16600 Max. :41.4900 Max. :29.0200 Max. :10.22000 Max. :10.57000
Global_Sales
Min. : 0.0100
1st Qu.: 0.0600
Median : 0.1700
Mean : 0.5374
3rd Qu.: 0.4700
Max. :82.7400
variance of numeric data:
var(dataset$NA_Sales)
[1] 0.6669712
var(dataset$EU_Sales)
[1] 0.2553799
var(dataset$JP_Sales)
[1] 0.0956607
var(dataset$Other_Sales)
[1] 0.03556559
var(dataset$Global_Sales)
[1] 2.418112
Graphs:
dataset2 <- dataset %>% sample_n(50)
tab <- dataset2$Platform %>% table()
precentages <- tab %>% prop.table() %>% round(3) * 100
txt <- paste0(names(tab), '\n', precentages, '%')
pie(tab, labels=txt , main = "Pie chart of Platform")

This pie chart illustrate platforms of global video games , We notice
from the pie chart of platform attribute that releasing a game for PS
users will increase the popularity of the game since it is the most
common platform among gamers.
# coloring barplot and adding text
tab<-dataset$Genre %>% table()
precentages<-tab %>% prop.table() %>% round(3)*100
txt<-paste0(names(tab), '\n',precentages,'%')
bb <- dataset$Genre %>% table() %>% barplot(axisnames=F, main = "Barplot for Popular genres ",ylab='count',col=c('pink','blue','lightblue','green','lightgreen','red','orange','red','grey','yellow','azure','olivedrab'))
text(bb,tab/2,labels=txt,cex=1.5)

This barplot illustrates popularity of global video games genres ,In
terms of genre, action games are the most popular, followed by sports
and music games. It is safe to assume that a high number of genres of
this nature exist due to their popularity and sales.
boxplot(dataset$NA_Sales , main="
BoxPlot for NA_Sales")

The boxplot of the NA_Sales (Sales of the game in north America)
attribute indicates that the values are close to each other ,and there
are a lot of outliers since the dataset represents all the north America
sales of video games.
boxplot(dataset$EU_Sales, main="
BoxPlot for EU_Sales")

The boxplot of the EU_Sales (sales of the game in Europe) attribute
indicates that the values are close to each other, and there are a lot
of outliers since the dataset represents all the Europe sales of video
games.
boxplot(dataset$JP_Sales , main="
BoxPlot for JP_Sales")

The boxplot of the JP_Sales (sales of the game in Japan) attribute
indicates that the values are close to each other, and there are a lot
of outliers since the dataset represents all the Japan sales of video
games.
boxplot(dataset$Other_Sales , main="
BoxPlot for Other_Sales")

The boxplot of the Other-sales attribute indicate that the values are
close to each other ,and there is a lot of outliers since the dataset
represents the global sales of video games.
boxplot(dataset$Global_Sales , main="BoxPlot for Global_Sales")

The boxplot of the Global-sales attribute indicate that the values
are close to each other ,and there is a lot of outliers since the
dataset represents the global sales of video games.
qplot(data = dataset, x=Global_Sales,y=Genre,fill=I("yellow"),width=0.5 ,geom = "boxplot" , main = "BoxPlots for genre and Global_Sales")
Warning: `qplot()` was deprecated in ggplot2 3.4.0.

In the boxplot we can see that all the genres have Glob_ sales close
to each other, but we notice an outlier that reaches more than 80 Glob_
sales which is a game with genre sports.
dataset$Year %>% table() %>% barplot( main = "Barplot for year")

The barplot of year illustrate that the number of video games were
low from 1980 until 2000 , then number of games increased to more than
1200 till 2012.
pairs(~NA_Sales + EU_Sales + JP_Sales + Other_Sales + Global_Sales, data = dataset,
main = "Sales Scatterplot")

We used Scatterplot to determine the type of correlation we have
between the sales; we can see that the majority have positive
correlation with each other.
(Pre - processing):
Null checking:
we checked nulls values to know how many nulls values we have, so we
can determine how we will deal with them.
sum(is.na(dataset$Rank))
[1] 0
NullRank<-dataset[dataset$Rank=="N/A",]
NullRank
checking for nulls in Rank (there is no nulls)
sum(is.na(dataset$Name))
[1] 0
NullName<-dataset[dataset$Name=="N/A",]
NullName
checking for nulls in name (there is no nulls)
sum(is.na(dataset$Platform))
[1] 0
NullPlatform<-dataset[dataset$Platform=="N/A",]
checking for nulls in Platform(there is no nulls)
sum(is.na(dataset$Year))
[1] 0
NullYear<-dataset[dataset$Year=="N/A",]
NullYear
checking for nulls in year we won’t delete the null and we will leave
them as global constant because we want the sales data out of them.
sum(is.na(dataset$Genre))
[1] 0
NullGenre<-dataset[dataset$Genre=="N/A",]
NullGenre
checking for nulls in Genre(there is no nulls)
sum(is.na(dataset$Publisher))
[1] 0
NullPublisher<-dataset[dataset$Publisher=="N/A",]
NullPublisher
checking for nulls in Publisher. we won’t delete the null and we will
leave them as global constant as it is because we want the sales data of
them.
sum(is.na(dataset$NA_Sales))
[1] 0
NullNA_Sales<-dataset[dataset$NA_Sales=="N/A",]
NullNA_Sales
checking for nulls in NA_Sales (there is no nulls)
sum(is.na(dataset$EU_Sales))
[1] 0
NullEU_Sales<-dataset[dataset$EU_Sales=="N/A",]
NullEU_Sales
checking for nulls in EU_Sales (there is no nulls)
sum(is.na(dataset$JP_Sales))
[1] 0
NullJP_Sales<-dataset[dataset$JP_Sales=="N/A",]
NullJP_Sales
checking for nulls in JP_Sales (there is no nulls)
sum(is.na(dataset$Other_Sales))
[1] 0
NullOther_Sales<-dataset[dataset$Other_Sales=="N/A",]
There is no null values in the other_sales.
sum(is.na(dataset$Global_Sales))
[1] 0
NullGlobal_Sales<-dataset[dataset$Global_Saless=="N/A",]
There is no null values in the Global_Sales.
Encoding:
We will encode our categorical data since most machine learning
algorithms work with numbers rather than text.
dataset$Platform=factor(dataset$Platform,levels=c("2600","3DO","3DS","DC","DS","GB","GBA","GC","GEN","GG","N64","NES","NG","PC","PCFX","PS","PS2","PS3","PS4","PSP","PSV","SAT","SCD","SNES","TG16","Wii","WiiU","WS","X360","XB","XOne"), labels=c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31))
this column will be encoded to facilitate our data mining task.
dataset$Genre=factor(dataset$Genre,levels=c("Action","Adventure","Fighting","Platform","Puzzle","Racing","Role-Playing","Shooter","Simulation","Sports","Strategy","Misc"),labels=c(1,2,3,4,5,6,7,8,9,10,11,12))
Since most machine learning algorithms work with numbers and not with
text or categorical variables, this column will be encoded to facilitate
our data mining task.
Outliers:
Analyses and statistical models can be ruined by outliers, making it
difficult to detect a true effect. Therefore, we are checking for them
and removing them if we find any.
outlier of NA_Sales
OutNA_Sales = outlier(dataset$NA_Sales, logical =TRUE)
sum(OutNA_Sales)
[1] 1
Find_outlier = which(OutNA_Sales ==TRUE, arr.ind = TRUE)
outlier of EU_Sales
OutEU_Sales = outlier(dataset$EU_Sales, logical =TRUE)
sum(OutEU_Sales)
[1] 1
Find_outlier = which(OutEU_Sales ==TRUE, arr.ind = TRUE)
outlier of JP_Sales
OutJP_Sales = outlier(dataset$JP_Sales, logical =TRUE)
sum(OutJP_Sales)
[1] 1
Find_outlier = which(OutJP_Sales ==TRUE, arr.ind = TRUE)
outlier of other_sales
OutOS=outlier(dataset$Other_Sales, logical=TRUE)
sum(OutOS)
[1] 1
Find_outlier=which(OutOS==TRUE, arr.ind=TRUE)
outlier of Global_sales
OutGS=outlier(dataset$Global_Sales, logical=TRUE)
sum(OutGS)
[1] 1
Find_outlier=which(OutGS==TRUE, arr.ind=TRUE)
Remove outliers
dataset= dataset[-Find_outlier,]
Normalization:
The normalization of data will improve the performance of many
machine learning algorithms by accounting for differences in the scale
of the input features.
Dataset before normalization:
datsetWithoutNormalization<-dataset
normalize <- function(x) {return ((x - min(x)) / (max(x) - min(x)))}
dataset$NA_Sales<-normalize(datsetWithoutNormalization$NA_Sales)
dataset$EU_Sales<-normalize(datsetWithoutNormalization$EU_Sales)
dataset$JP_Sales<-normalize(datsetWithoutNormalization$JP_Sales)
dataset$Other_Sales<-normalize(datsetWithoutNormalization$Other_Sales)
dataset$Global_Sales<-normalize(datsetWithoutNormalization$Global_Sales)
We chose min-max normalization instead of z-score normalization
because min-max transform the data into a specific range, which enhances
its suitability for visualization and comparison. Additionally, it
simplifies the process of assessing attribute importance and their
contributions to the model.
Feautre selection:
Our class label (popular) refers to Global_Sales.because we have
multiple regions sales we chose to evaluate each region sales based on
their importance to (global_sales) column,and those that are less
important will be deleted from the dataset.
Use roc_curve area as score
roc_imp <- filterVarImp(x = dataset[,7:10], y = dataset$Global_Sales)
Sort the score in decreasing order
roc_imp <- data.frame(cbind(variable = rownames(roc_imp), score = roc_imp[,1]))
roc_imp$score <- as.double(roc_imp$score)
roc_imp[order(roc_imp$score,decreasing = TRUE),]
we will remove the (JP_Sales) because it is of low importance to our
class_label(Global_Sales)
dataset<- dataset[,-9]
Dataset after pre-processing:
print(dataset)
LS0tCnRpdGxlOiAiR2xvYmFsIHZpZGVvIGdhbWVzIHNhbGVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgUGhhc2UxOgojIFByb3BsZW06CldlIGxpdmUgaW4gYSB0aW1lIHdoZW4gdmlkZW8gZ2FtZXMgYXJlIGV4dHJlbWVseSBwb3B1bGFyLiBUaGUgZ2xvYmFsIHZpZGVvIGdhbWUgbWFya2V0IGNvbnRpbnVlcyB0byBncm93IHllYXItb24teWVhciwgYW5kIHRoZSBpbmR1c3RyeSBpcyBub3cgdmFsdWVkIGF0IG92ZXIgJDEwMCBiaWxsaW9uIHdvcmxkd2lkZS4gV2l0aCB0ZWNobm9sb2d5IGNvbnRpbnVvdXNseSBwdXNoaW5nIHRoZSBib3VuZGFyaWVzLCB2aWRlbyBnYW1lcyBoYXZlIG9ubHkgYmVjb21lIG1vcmUgcG9wdWxhciBhbmQgbW9yZSBoaWdoLXF1YWxpdHkuIEdhbWVwbGF5IG1lY2hhbmljcywgY3V0dGluZy1lZGdlIGdyYXBoaWNzLCBhbmQgaW50cmljYXRlIHN0b3J5bGluZXMgbWFrZSB0b2RheSdzIGdhbWVzIG1vcmUgaW1tZXJzaXZlIHRoYW4gZXZlciBiZWZvcmUuIFdlIGNob3NlIHRoaXMgZGF0YXNldCB0byBnYWluIGluc2lnaHRzIG9uIHRoZSBwb3B1bGFyaXR5IG9mIHVwY29taW5nIGdhbWVzLiAKCgojIENsYXNzIGxhYmVsOgpQb3B1bGFyJyBpcyBvdXIgY2xhc3MgbGFiZWwsIHdlIHdpbGwgdXNlIEdsb2JhbF9TYWxlcyBhdHRyaWJ1dGUgdG8gcHJlZGljdCB3aGV0aGVyIGEgZ2FtZSB3aWxsIHNlbGwgMTAwMDAwMCBvciBtb3JlIGdsb2JhbGx5LiAKCgojIERhdGEgTWluaW5nIFRhc2s6Ck91ciBkYXRhIG1pbmluZyB0YXNrIGlzIHRvIHByZWRpY3QgdGhlIHBvcHVsYXJpdHkgb2YgdXBjb21pbmcgZ2FtZXMgdXNpbmcgcmVncmVzc2lvbi4KCgojIERlc2NyaXB0aW9uIG9mIHRoZSBkYXRhc2V0OgpUaGUgZGF0YXNldCBwcm92aWRlZCBieSB2Z2NoYXJ0ei5jb20gc3VwcGx5IHVzIHdpdGggYSB2YWx1YWJsZSByZXNvdXJjZSB0byBleHBsb3JlIHRoZSBwbGF0Zm9ybXMgYW5kIGdlbnJlcyBvZiB0aGUgdG9wIDE2NTk5IGdsb2JhbCB2aWRlbyBnYW1lcy4gVGhyb3VnaCBpdCwgd2UgY2FuIGFuYWx5emUgdGhlIG1vc3QgcG9wdWxhciBwbGF0Zm9ybXMgYW5kIGdlbnJlcyB0aGF0IGFyZSBpbmZsdWVuY2luZyBnbG9iYWwgc2FsZXMsIGFuZCBkZXRlY3RyIGhvdyByZWdpb25zJyBzYWxlcyBhZmZlY3QgZ2xvYmFsIHNhbGVzLiAKCiMgT3VyIGdvYWw6CgpPdXIgZ29hbCAgZnJvbSBzdHVkeWluZyB0aGlzIGRhdGFzZXQgaXMgdG8gdXRpbGl6ZSByZWdyZXNzaW9uIHRlY2huaXF1ZXMgb24gdGhlIGlucHV0IGRhdGEgdG8gbWFrZSBwcmVkaWN0aW9ucyBhYm91dCB0aGUgcG9wdWxhcml0eSBvZiB1cGNvbWluZyBnYW1lcy4KCiMgU291cmNlIGFuZCBsaW5rOgpTb3VyY2U6IEthZ2dsZQoKVVJMIGxpbms6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvZ3JlZ29ydXQvdmlkZW9nYW1lc2FsZXMKCgoKCiMgQXR0cmlidXRlcyBkZXNjcmlwdGlvbjoKCgp8ICoqQXR0cmlidXRlcyBuYW1lKiogfCAqKkRlc2NyaXB0aW9uKiogICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwgCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfFJhbmsgICAgICAgICAgICAgICB8IFJhbmtpbmcgb2YgdGhlIGdhbWUgYmFzZWQgb24gZ2xvYmFsIHNhbGVzLiB8IE51bWVyaWMgICAgICAgfAp8IE5hbWUgICAgICAgICAgICB8IE5hbWUgb2YgdGhlIGdhbWUuIHwgTm9taW5hbCAgICAgICB8IAp8IFBsYXRmb3JtICAgICAgfCBQbGF0Zm9ybSB0aGUgZ2FtZSB3YXMgcmVsZWFzZWQgb24uIHwgTm9taW5hbCAgICAgICB8IAp8IFllYXIgICAgICAgICAgICAgICB8IFllYXIgdGhlIGdhbWUgd2FzIHJlbGVhc2VkLiB8IE9yZGluYWwgICAgICAgfCAKfCBHZW5yZSAgICAgICAgICAgIHwgR2VucmUgb2YgdGhlIGdhbWUgfCBOb21pbmFsICAgICAgIHwgCnwgUHVibGlzaGVyICAgICAgfCBQdWJsaXNoZXIgb2YgdGhlIGdhbWUuIHwgTm9taW5hbCAgICAgICB8IAp8IE5BX1NhbGVzICAgICAgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBOb3J0aCBBbWVyaWNhIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICB8IAp8IEVVX1NhbGVzICAgICAgIHwgU2FsZXMgb2YgdGhlIGdhbWUgaW4gRXVyb3BlIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICAgfCAKfCBKUF9TYWxlcyAgICAgICAgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBKYXBhbiB8IE51bWVyaWMgKHJhdGlvLXNjYWxlZCkgICAgICAgIHwgCnwgT3RoZXJfU2FsZXMgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBvdGhlciByZWdpb25zIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICAgfCAKfCBHbG9iYWxfU2FsZXMgIHwgVG90YWwgc2FsZXMgb2YgdGhlIGdhbWUgd29ybGR3aWRlIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgfCAgICAgCgoKCgoKCiMgbG9hZGluZyBsaWJyYXJpZXMgbmVlZGVkIGZvciBvdXIgZGF0YSBtaW5pbmcgdGFza3M6CmBgYHtyfQpsaWJyYXJ5KG91dGxpZXJzKSAKbGlicmFyeShkcGx5cikKbGlicmFyeShIbWlzYykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG1sYmVuY2gpCmxpYnJhcnkoY2FyZXQpCm9wdGlvbnMobWF4LnByaW50PTk5OTk5OTkpCmBgYAoKCgoKCiMgSW1wb3J0aW5nIG91ciBkYXRhc2V0OgpgYGB7cn0KZGF0YXNldD1yZWFkLmNzdigiRGF0YXNldC92Z3NhbGVzLmNzdiIpCmBgYAoKCgoKIyBHZW5lcmFsIGluZm8gYWJvdXQgb3VyIGRhdGFzZXQ6CgpjaGVja2luZyBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucywgYW5kIGNoZWtpbmcgZGltZW5zaW9uYWxpdHkgYW5kIGNvdWx1bW5zIG5hbWVzOgpgYGB7cn0KbnJvdyhkYXRhc2V0KQpuY29sKGRhdGFzZXQpCmRpbShkYXRhc2V0KQpuYW1lcyhkYXRhc2V0KQpgYGAKCgoKCkRhdGFzZXQgc3RydWN0dXJlOgpgYGB7cn0Kc3RyKGRhdGFzZXQpCmBgYAoKCgpzYW1wbGUgb2YgcmF3IGRhdGFzZXQoZmlyc3QgMTAgcm93cyk6CmBgYHtyfQpoZWFkKGRhdGFzZXQsIDEwKQpgYGAKCnNhbXBsZSBvZiByYXcgZGF0YXNldChsYXN0IDEwIHJvd3MpOgpgYGB7cn0KdGFpbChkYXRhc2V0LCAxMCkKYGBgCgpGaXZlIG51bWJlciBzdW1tYXJ5IG9mIGVhY2ggYXR0cmlidXRlIGluIG91ciBkYXRhc2V0OgpgYGB7cn0Kc3VtbWFyeShkYXRhc2V0KQpgYGAKCnZhcmlhbmNlIG9mIG51bWVyaWMgZGF0YToKYGBge3J9CnZhcihkYXRhc2V0JE5BX1NhbGVzKQp2YXIoZGF0YXNldCRFVV9TYWxlcykKdmFyKGRhdGFzZXQkSlBfU2FsZXMpCnZhcihkYXRhc2V0JE90aGVyX1NhbGVzKQp2YXIoZGF0YXNldCRHbG9iYWxfU2FsZXMpCmBgYAoKCgoKCgojIEdyYXBoczoKCmBgYHtyfQpkYXRhc2V0MiA8LSBkYXRhc2V0ICU+JSBzYW1wbGVfbig1MCkKdGFiIDwtIGRhdGFzZXQyJFBsYXRmb3JtICU+JSB0YWJsZSgpCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCAKdHh0IDwtIHBhc3RlMChuYW1lcyh0YWIpLCAnXG4nLCBwcmVjZW50YWdlcywgJyUnKSAKCnBpZSh0YWIsIGxhYmVscz10eHQgLCBtYWluID0gIlBpZSBjaGFydCBvZiBQbGF0Zm9ybSIpIAoKYGBgCgpUaGlzIHBpZSBjaGFydCBpbGx1c3RyYXRlIHBsYXRmb3JtcyBvZiBnbG9iYWwgdmlkZW8gZ2FtZXMgLCBXZSBub3RpY2UgZnJvbSB0aGUgcGllIGNoYXJ0IG9mIHBsYXRmb3JtIGF0dHJpYnV0ZSB0aGF0IHJlbGVhc2luZyBhIGdhbWUgZm9yIFBTIHVzZXJzIHdpbGwgaW5jcmVhc2UgdGhlIHBvcHVsYXJpdHkgb2YgdGhlIGdhbWUgc2luY2UgaXQgaXMgdGhlIG1vc3QgY29tbW9uIHBsYXRmb3JtIGFtb25nIGdhbWVycy4gCgoKCgoKYGBge3J9CiMgY29sb3JpbmcgYmFycGxvdCBhbmQgYWRkaW5nIHRleHQKdGFiPC1kYXRhc2V0JEdlbnJlICU+JSB0YWJsZSgpIAoKcHJlY2VudGFnZXM8LXRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSoxMDAgCgp0eHQ8LXBhc3RlMChuYW1lcyh0YWIpLCAnXG4nLHByZWNlbnRhZ2VzLCclJykgCgpiYiA8LSBkYXRhc2V0JEdlbnJlICU+JSB0YWJsZSgpICU+JSBiYXJwbG90KGF4aXNuYW1lcz1GLCBtYWluID0gIkJhcnBsb3QgZm9yIFBvcHVsYXIgZ2VucmVzICIseWxhYj0nY291bnQnLGNvbD1jKCdwaW5rJywnYmx1ZScsJ2xpZ2h0Ymx1ZScsJ2dyZWVuJywnbGlnaHRncmVlbicsJ3JlZCcsJ29yYW5nZScsJ3JlZCcsJ2dyZXknLCd5ZWxsb3cnLCdhenVyZScsJ29saXZlZHJhYicpKSAKCnRleHQoYmIsdGFiLzIsbGFiZWxzPXR4dCxjZXg9MS41KSAKYGBgClRoaXMgYmFycGxvdCBpbGx1c3RyYXRlcyBwb3B1bGFyaXR5IG9mIGdsb2JhbCB2aWRlbyBnYW1lcyBnZW5yZXMgLEluIHRlcm1zIG9mIGdlbnJlLCBhY3Rpb24gZ2FtZXMgYXJlIHRoZSBtb3N0IHBvcHVsYXIsIGZvbGxvd2VkIGJ5IHNwb3J0cyBhbmQgbXVzaWMgZ2FtZXMuIEl0IGlzIHNhZmUgdG8gYXNzdW1lIHRoYXQgYSBoaWdoIG51bWJlciBvZiBnZW5yZXMgb2YgdGhpcyBuYXR1cmUgZXhpc3QgZHVlIHRvIHRoZWlyIHBvcHVsYXJpdHkgYW5kIHNhbGVzLgoKCgoKCmBgYHtyfQpib3hwbG90KGRhdGFzZXQkTkFfU2FsZXMgLCBtYWluPSIKQm94UGxvdCBmb3IgTkFfU2FsZXMiKQpgYGAKVGhlIGJveHBsb3Qgb2YgdGhlIE5BX1NhbGVzICAoU2FsZXMgb2YgdGhlIGdhbWUgaW4gbm9ydGggQW1lcmljYSkgYXR0cmlidXRlIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIgLGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2Ygb3V0bGllcnMgc2luY2UgdGhlIGRhdGFzZXQgcmVwcmVzZW50cyBhbGwgdGhlIG5vcnRoIEFtZXJpY2Egc2FsZXMgb2YgdmlkZW8gZ2FtZXMuCgpgYGB7cn0KYm94cGxvdChkYXRhc2V0JEVVX1NhbGVzLCBtYWluPSIKIEJveFBsb3QgZm9yIEVVX1NhbGVzIikKYGBgClRoZSBib3hwbG90IG9mIHRoZSBFVV9TYWxlcyAoc2FsZXMgb2YgdGhlIGdhbWUgaW4gRXVyb3BlKSBhdHRyaWJ1dGUgaW5kaWNhdGVzIHRoYXQgdGhlIHZhbHVlcyBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlciwgYW5kIHRoZXJlIGFyZSBhIGxvdCBvZiBvdXRsaWVycyBzaW5jZSB0aGUgZGF0YXNldCByZXByZXNlbnRzIGFsbCB0aGUgRXVyb3BlIHNhbGVzIG9mIHZpZGVvIGdhbWVzLgoKYGBge3J9CmJveHBsb3QoZGF0YXNldCRKUF9TYWxlcyAsIG1haW49IgogQm94UGxvdCBmb3IgSlBfU2FsZXMiKQpgYGAKVGhlIGJveHBsb3Qgb2YgdGhlIEpQX1NhbGVzIChzYWxlcyBvZiB0aGUgZ2FtZSBpbiBKYXBhbikgYXR0cmlidXRlIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIsIGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2Ygb3V0bGllcnMgc2luY2UgdGhlIGRhdGFzZXQgcmVwcmVzZW50cyBhbGwgdGhlIEphcGFuIHNhbGVzIG9mIHZpZGVvIGdhbWVzLgoKCmBgYHtyfQpib3hwbG90KGRhdGFzZXQkT3RoZXJfU2FsZXMgLCBtYWluPSIKIEJveFBsb3QgZm9yIE90aGVyX1NhbGVzIikgCmBgYCAgCgpUaGUgYm94cGxvdCBvZiB0aGUgT3RoZXItc2FsZXMgYXR0cmlidXRlIGluZGljYXRlIHRoYXQgdGhlIHZhbHVlcyBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlciAsYW5kIHRoZXJlIGlzIGEgbG90IG9mIG91dGxpZXJzIHNpbmNlIHRoZSBkYXRhc2V0IHJlcHJlc2VudHMgdGhlIGdsb2JhbCBzYWxlcyBvZiB2aWRlbyBnYW1lcy4gCgoKCgpgYGB7cn0KYm94cGxvdChkYXRhc2V0JEdsb2JhbF9TYWxlcyAsIG1haW49IkJveFBsb3QgZm9yIEdsb2JhbF9TYWxlcyIpCgpgYGAgIApUaGUgYm94cGxvdCBvZiB0aGUgR2xvYmFsLXNhbGVzIGF0dHJpYnV0ZSBpbmRpY2F0ZSB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIgLGFuZCB0aGVyZSBpcyBhIGxvdCBvZiBvdXRsaWVycyBzaW5jZSB0aGUgZGF0YXNldCByZXByZXNlbnRzIHRoZSBnbG9iYWwgc2FsZXMgb2YgdmlkZW8gZ2FtZXMuIAoKCgpgYGB7cn0KcXBsb3QoZGF0YSA9IGRhdGFzZXQsIHg9R2xvYmFsX1NhbGVzLHk9R2VucmUsZmlsbD1JKCJ5ZWxsb3ciKSx3aWR0aD0wLjUgLGdlb20gPSAiYm94cGxvdCIgLCBtYWluID0gIkJveFBsb3RzIGZvciBnZW5yZSBhbmQgR2xvYmFsX1NhbGVzIikKYGBgCgpJbiB0aGUgYm94cGxvdCB3ZSBjYW4gc2VlIHRoYXQgYWxsIHRoZSBnZW5yZXMgaGF2ZSBHbG9iXyBzYWxlcyBjbG9zZSB0byBlYWNoIG90aGVyLCBidXQgd2Ugbm90aWNlIGFuIG91dGxpZXIgdGhhdCByZWFjaGVzIG1vcmUgdGhhbiA4MCBHbG9iXyBzYWxlcyB3aGljaCBpcyBhIGdhbWUgd2l0aCBnZW5yZSBzcG9ydHMuIAoKYGBge3J9CmRhdGFzZXQkWWVhciAlPiUgdGFibGUoKSAlPiUgYmFycGxvdCggbWFpbiA9ICJCYXJwbG90IGZvciB5ZWFyIikKYGBgCgogVGhlIGJhcnBsb3Qgb2YgeWVhciBpbGx1c3RyYXRlIHRoYXQgdGhlIG51bWJlciBvZiB2aWRlbyBnYW1lcyB3ZXJlIGxvdyBmcm9tIDE5ODAgdW50aWwgMjAwMCAsIHRoZW4gbnVtYmVyIG9mIGdhbWVzIGluY3JlYXNlZCB0byBtb3JlIHRoYW4gMTIwMCB0aWxsIDIwMTIuCgoKYGBge3J9CnBhaXJzKH5OQV9TYWxlcyArIEVVX1NhbGVzICsgSlBfU2FsZXMgKyBPdGhlcl9TYWxlcyArIEdsb2JhbF9TYWxlcywgZGF0YSA9IGRhdGFzZXQsCiAgICAgIG1haW4gPSAiU2FsZXMgU2NhdHRlcnBsb3QiKQpgYGAgICAgCldlIHVzZWQgU2NhdHRlcnBsb3QgdG8gZGV0ZXJtaW5lIHRoZSB0eXBlIG9mIGNvcnJlbGF0aW9uIHdlIGhhdmUgYmV0d2VlbiB0aGUgc2FsZXM7IHdlIGNhbiBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgaGF2ZSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoIGVhY2ggb3RoZXIuIAogCiAKICAgICAgCiMgKFByZSAtIHByb2Nlc3NpbmcpOgoKIyBWYXJhaWJsZSB0cmFuc2Zvcm1hdGlvbjoKYGBge3J9CmRhdGFzZXQkUmFuaz1hcy5jaGFyYWN0ZXIoZGF0YXNldCRSYW5rKQpgYGAKV2UgdHJhbnNmb3JtZWQgdGhlIFJhbmsgZnJvbSBudW1yaWMgdG8gY2hhcixiZWNhdXNlIHdlIHdpbGwgdXNlIHRoZW0gYXMgb3JkaW5hbCBkYXRhLgoKIyBOdWxsIGNoZWNraW5nOgp3ZSBjaGVja2VkIG51bGxzIHZhbHVlcyB0byBrbm93IGhvdyBtYW55IG51bGxzIHZhbHVlcyB3ZSBoYXZlLCBzbyB3ZSBjYW4gZGV0ZXJtaW5lIGhvdyB3ZSB3aWxsIGRlYWwgd2l0aCB0aGVtLgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkUmFuaykpCk51bGxSYW5rPC1kYXRhc2V0W2RhdGFzZXQkUmFuaz09Ik4vQSIsXQpOdWxsUmFuawpgYGAKY2hlY2tpbmcgZm9yIG51bGxzIGluIFJhbmsgKHRoZXJlIGlzIG5vIG51bGxzKQpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkTmFtZSkpCk51bGxOYW1lPC1kYXRhc2V0W2RhdGFzZXQkTmFtZT09Ik4vQSIsXQpOdWxsTmFtZQpgYGAKCmNoZWNraW5nIGZvciBudWxscyBpbiBuYW1lICh0aGVyZSBpcyBubyBudWxscykKCmBgYHtyfQpzdW0oaXMubmEoZGF0YXNldCRQbGF0Zm9ybSkpCk51bGxQbGF0Zm9ybTwtZGF0YXNldFtkYXRhc2V0JFBsYXRmb3JtPT0iTi9BIixdCgoKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBQbGF0Zm9ybSh0aGVyZSBpcyBubyBudWxscykKCmBgYHtyfQpzdW0oaXMubmEoZGF0YXNldCRZZWFyKSkKTnVsbFllYXI8LWRhdGFzZXRbZGF0YXNldCRZZWFyPT0iTi9BIixdCk51bGxZZWFyCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4geWVhcgp3ZSB3b24ndCBkZWxldGUgdGhlIG51bGwgYW5kIHdlIHdpbGwgbGVhdmUgdGhlbSBhcyBnbG9iYWwgY29uc3RhbnQgYmVjYXVzZSB3ZSB3YW50IHRoZSBzYWxlcyBkYXRhIG91dCBvZiB0aGVtLgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JEdlbnJlKSkKTnVsbEdlbnJlPC1kYXRhc2V0W2RhdGFzZXQkR2VucmU9PSJOL0EiLF0KTnVsbEdlbnJlCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4gR2VucmUodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkUHVibGlzaGVyKSkKTnVsbFB1Ymxpc2hlcjwtZGF0YXNldFtkYXRhc2V0JFB1Ymxpc2hlcj09Ik4vQSIsXQpOdWxsUHVibGlzaGVyCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4gUHVibGlzaGVyLgp3ZSB3b24ndCBkZWxldGUgdGhlIG51bGwgYW5kIHdlIHdpbGwgbGVhdmUgdGhlbSBhcyBnbG9iYWwgY29uc3RhbnQgYXMgaXQgaXMgYmVjYXVzZSB3ZSB3YW50IHRoZSBzYWxlcyBkYXRhIG9mIHRoZW0uCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkTkFfU2FsZXMpKQpOdWxsTkFfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCROQV9TYWxlcz09Ik4vQSIsXQpOdWxsTkFfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBOQV9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkRVVfU2FsZXMpKQpOdWxsRVVfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRFVV9TYWxlcz09Ik4vQSIsXQpOdWxsRVVfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBFVV9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkSlBfU2FsZXMpKQpOdWxsSlBfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRKUF9TYWxlcz09Ik4vQSIsXQpOdWxsSlBfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBKUF9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JE90aGVyX1NhbGVzKSkKTnVsbE90aGVyX1NhbGVzPC1kYXRhc2V0W2RhdGFzZXQkT3RoZXJfU2FsZXM9PSJOL0EiLF0KCgpgYGAKVGhlcmUgaXMgbm8gbnVsbCB2YWx1ZXMgaW4gdGhlIG90aGVyX3NhbGVzLgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JEdsb2JhbF9TYWxlcykpCk51bGxHbG9iYWxfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRHbG9iYWxfU2FsZXNzPT0iTi9BIixdCgoKYGBgClRoZXJlIGlzIG5vIG51bGwgdmFsdWVzIGluIHRoZSBHbG9iYWxfU2FsZXMuCgojIEVuY29kaW5nOgpXZSB3aWxsIGVuY29kZSBvdXIgY2F0ZWdvcmljYWwgZGF0YSBzaW5jZSBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB3b3JrIHdpdGggbnVtYmVycyByYXRoZXIgdGhhbiB0ZXh0LgoKYGBge3J9CmRhdGFzZXQkUGxhdGZvcm09ZmFjdG9yKGRhdGFzZXQkUGxhdGZvcm0sbGV2ZWxzPWMoIjI2MDAiLCIzRE8iLCIzRFMiLCJEQyIsIkRTIiwiR0IiLCJHQkEiLCJHQyIsIkdFTiIsIkdHIiwiTjY0IiwiTkVTIiwiTkciLCJQQyIsIlBDRlgiLCJQUyIsIlBTMiIsIlBTMyIsIlBTNCIsIlBTUCIsIlBTViIsIlNBVCIsIlNDRCIsIlNORVMiLCJURzE2IiwiV2lpIiwiV2lpVSIsIldTIiwiWDM2MCIsIlhCIiwiWE9uZSIpLCBsYWJlbHM9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwxOSwyMCwyMSwyMiwyMywyNCwyNSwyNiwyNywyOCwyOSwzMCwzMSkpCmBgYAp0aGlzIGNvbHVtbiB3aWxsIGJlIGVuY29kZWQgdG8gZmFjaWxpdGF0ZSBvdXIgZGF0YSBtaW5pbmcgdGFzay4KCmBgYHtyfQpkYXRhc2V0JEdlbnJlPWZhY3RvcihkYXRhc2V0JEdlbnJlLGxldmVscz1jKCJBY3Rpb24iLCJBZHZlbnR1cmUiLCJGaWdodGluZyIsIlBsYXRmb3JtIiwiUHV6emxlIiwiUmFjaW5nIiwiUm9sZS1QbGF5aW5nIiwiU2hvb3RlciIsIlNpbXVsYXRpb24iLCJTcG9ydHMiLCJTdHJhdGVneSIsIk1pc2MiKSxsYWJlbHM9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMikpCmBgYApTaW5jZSBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB3b3JrIHdpdGggbnVtYmVycyBhbmQgbm90IHdpdGggdGV4dCBvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHRoaXMgY29sdW1uIHdpbGwgYmUgZW5jb2RlZCB0byBmYWNpbGl0YXRlIG91ciBkYXRhIG1pbmluZyB0YXNrLgoKIyBPdXRsaWVyczoKQW5hbHlzZXMgYW5kIHN0YXRpc3RpY2FsIG1vZGVscyBjYW4gYmUgcnVpbmVkIGJ5IG91dGxpZXJzLCBtYWtpbmcgaXQgZGlmZmljdWx0IHRvIGRldGVjdCBhIHRydWUgZWZmZWN0LiBUaGVyZWZvcmUsIHdlIGFyZSBjaGVja2luZyBmb3IgdGhlbSBhbmQgcmVtb3ZpbmcgdGhlbSBpZiB3ZSBmaW5kIGFueS4KCm91dGxpZXIgb2YgTkFfU2FsZXMKYGBge3J9Ck91dE5BX1NhbGVzID0gb3V0bGllcihkYXRhc2V0JE5BX1NhbGVzLCBsb2dpY2FsID1UUlVFKQpzdW0oT3V0TkFfU2FsZXMpCkZpbmRfb3V0bGllciA9IHdoaWNoKE91dE5BX1NhbGVzID09VFJVRSwgYXJyLmluZCA9IFRSVUUpCgpgYGAKb3V0bGllciBvZiBFVV9TYWxlcwpgYGB7cn0KT3V0RVVfU2FsZXMgPSBvdXRsaWVyKGRhdGFzZXQkRVVfU2FsZXMsIGxvZ2ljYWwgPVRSVUUpCnN1bShPdXRFVV9TYWxlcykKRmluZF9vdXRsaWVyID0gd2hpY2goT3V0RVVfU2FsZXMgPT1UUlVFLCBhcnIuaW5kID0gVFJVRSkKYGBgCm91dGxpZXIgb2YgSlBfU2FsZXMKYGBge3J9Ck91dEpQX1NhbGVzID0gb3V0bGllcihkYXRhc2V0JEpQX1NhbGVzLCBsb2dpY2FsID1UUlVFKQpzdW0oT3V0SlBfU2FsZXMpCkZpbmRfb3V0bGllciA9IHdoaWNoKE91dEpQX1NhbGVzID09VFJVRSwgYXJyLmluZCA9IFRSVUUpCmBgYAoKb3V0bGllciBvZiBvdGhlcl9zYWxlcyAKYGBge3J9Ck91dE9TPW91dGxpZXIoZGF0YXNldCRPdGhlcl9TYWxlcywgbG9naWNhbD1UUlVFKSAgCnN1bShPdXRPUykgIApGaW5kX291dGxpZXI9d2hpY2goT3V0T1M9PVRSVUUsIGFyci5pbmQ9VFJVRSkgIAoKYGBgCgoKb3V0bGllciBvZiBHbG9iYWxfc2FsZXMgCgpgYGB7cn0KT3V0R1M9b3V0bGllcihkYXRhc2V0JEdsb2JhbF9TYWxlcywgbG9naWNhbD1UUlVFKSAgCnN1bShPdXRHUykgIApGaW5kX291dGxpZXI9d2hpY2goT3V0R1M9PVRSVUUsIGFyci5pbmQ9VFJVRSkgIAoKYGBgCgoKCiMgUmVtb3ZlIG91dGxpZXJzIApgYGB7cn0KZGF0YXNldD0gZGF0YXNldFstRmluZF9vdXRsaWVyLF0KYGBgCgoKCiMgTm9ybWFsaXphdGlvbjoKVGhlIG5vcm1hbGl6YXRpb24gb2YgZGF0YSB3aWxsIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlIG9mIG1hbnkgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGJ5IGFjY291bnRpbmcgZm9yIGRpZmZlcmVuY2VzIGluIHRoZSBzY2FsZSBvZiB0aGUgaW5wdXQgZmVhdHVyZXMuCgpEYXRhc2V0IGJlZm9yZSBub3JtYWxpemF0aW9uOgpgYGB7cn0KZGF0c2V0V2l0aG91dE5vcm1hbGl6YXRpb248LWRhdGFzZXQKYGBgCgoKYGBge3J9Cm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7cmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSl9CmRhdGFzZXQkTkFfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiROQV9TYWxlcykKZGF0YXNldCRFVV9TYWxlczwtbm9ybWFsaXplKGRhdHNldFdpdGhvdXROb3JtYWxpemF0aW9uJEVVX1NhbGVzKQpkYXRhc2V0JEpQX1NhbGVzPC1ub3JtYWxpemUoZGF0c2V0V2l0aG91dE5vcm1hbGl6YXRpb24kSlBfU2FsZXMpCmRhdGFzZXQkT3RoZXJfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRPdGhlcl9TYWxlcykKZGF0YXNldCRHbG9iYWxfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRHbG9iYWxfU2FsZXMpCmBgYApXZSBjaG9zZSBtaW4tbWF4IG5vcm1hbGl6YXRpb24gaW5zdGVhZCBvZiB6LXNjb3JlIG5vcm1hbGl6YXRpb24gYmVjYXVzZSBtaW4tbWF4IHRyYW5zZm9ybSB0aGUgZGF0YSBpbnRvIGEgc3BlY2lmaWMgcmFuZ2UsIHdoaWNoIGVuaGFuY2VzIGl0cyBzdWl0YWJpbGl0eSBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY29tcGFyaXNvbi4gQWRkaXRpb25hbGx5LCBpdCBzaW1wbGlmaWVzIHRoZSBwcm9jZXNzIG9mIGFzc2Vzc2luZyBhdHRyaWJ1dGUgaW1wb3J0YW5jZSBhbmQgdGhlaXIgY29udHJpYnV0aW9ucyB0byB0aGUgbW9kZWwuCgoKCgoKIyBGZWF1dHJlIHNlbGVjdGlvbjoKCk91ciBjbGFzcyBsYWJlbCAocG9wdWxhcikgcmVmZXJzIHRvIEdsb2JhbF9TYWxlcy5iZWNhdXNlIHdlIGhhdmUgbXVsdGlwbGUgcmVnaW9ucyBzYWxlcyB3ZSBjaG9zZSB0byBldmFsdWF0ZSBlYWNoIHJlZ2lvbiBzYWxlcyBiYXNlZCBvbiB0aGVpciBpbXBvcnRhbmNlIHRvIChnbG9iYWxfc2FsZXMpIGNvbHVtbixhbmQgdGhvc2UgdGhhdCBhcmUgbGVzcyBpbXBvcnRhbnQgd2lsbCBiZSBkZWxldGVkIGZyb20gdGhlIGRhdGFzZXQuCgoKVXNlIHJvY19jdXJ2ZSBhcmVhIGFzIHNjb3JlCmBgYHtyfQpyb2NfaW1wIDwtIGZpbHRlclZhckltcCh4ID0gZGF0YXNldFssNzoxMF0sIHkgPSBkYXRhc2V0JEdsb2JhbF9TYWxlcykKYGBgCgoKU29ydCB0aGUgc2NvcmUgaW4gZGVjcmVhc2luZyBvcmRlcgpgYGB7cn0Kcm9jX2ltcCA8LSBkYXRhLmZyYW1lKGNiaW5kKHZhcmlhYmxlID0gcm93bmFtZXMocm9jX2ltcCksIHNjb3JlID0gcm9jX2ltcFssMV0pKQpyb2NfaW1wJHNjb3JlIDwtIGFzLmRvdWJsZShyb2NfaW1wJHNjb3JlKQpyb2NfaW1wW29yZGVyKHJvY19pbXAkc2NvcmUsZGVjcmVhc2luZyA9IFRSVUUpLF0KYGBgCgoKd2Ugd2lsbCByZW1vdmUgdGhlIChKUF9TYWxlcykgYmVjYXVzZSBpdCBpcyBvZiBsb3cgaW1wb3J0YW5jZSB0byBvdXIgY2xhc3NfbGFiZWwoR2xvYmFsX1NhbGVzKQpgYGB7cn0KZGF0YXNldDwtIGRhdGFzZXRbLC05XQpgYGAKCiMgRGF0YXNldCBhZnRlciBwcmUtcHJvY2Vzc2luZzoKYGBge3J9CnByaW50KGRhdGFzZXQpCmBgYA==